package com.iwdnb.gkgz.common.quota;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import com.alibaba.fastjson.JSON;

import com.google.common.collect.Lists;
import com.iwdnb.gkgz.common.model.dto.StockDayData;
import com.iwdnb.gkgz.common.utils.BigDecimalUtils;
import com.iwdnb.gkgz.common.utils.StockUtils;

/**
 * OBV（On-Balance Volume，累积能量线）是一个常用的技术分析指标，通过市场成交量的变化来反映价格趋势。OBV指标的计算可以分为以下几个步骤：
 *
 * 如果当天的收盘价高于前一天的收盘价，则当日的OBV = 前一日的OBV + 当日的成交量。
 * 如果当天的收盘价低于前一天的收盘价，则当日的OBV = 前一日的OBV - 当日的成交量。
 * 如果当天的收盘价等于前一天的收盘价，则当日的OBV = 前一日的OBV。
 */
public class OBVIndicator {

    public static final String QUOTA_NAME = "obv";

    public static void calculateOBV(List<StockDayData> stockDayDataList) {
        //计算成交量比
        //VolumeRatioIndicator.calculateVolumeRatio(stockDayDataList);
        List<Double> obvList = calculateExpOBV(stockDayDataList, 0.35);
        //List<Double> obvList = doCalculateOBV(stockDayDataList);

        int size = stockDayDataList.size();
        for (int i = 0; i < size; i++) {
            StockDayData data = stockDayDataList.get(i);
            Double obv = obvList.get(i);
            double tradeNum = data.getTradeNum();
            BigDecimal volumeRaio = StockUtils.getStockQuota(data, VolumeRatioIndicator.QUOTA_NAME);
            if (Objects.isNull(volumeRaio)) {
                volumeRaio = BigDecimal.ONE;
            }
            tradeNum = tradeNum / volumeRaio.doubleValue();
            if (tradeNum == 0) {
                tradeNum = data.getTradeNum();
            }
            BigDecimal obvData = BigDecimalUtils.divide(obv.toString(), tradeNum + "");
            StockUtils.setStockQuota(data, QUOTA_NAME, obvData);
        }

    }

    private static long getAverageTradeNum(List<StockDayData> stockDayDataList) {
        if (stockDayDataList.size() > 100) {
            stockDayDataList = stockDayDataList.subList(0, 100);
        }
        long sum = stockDayDataList.stream()
            .mapToLong(StockDayData::getTradeNum)
            .sum();
        return sum / stockDayDataList.size();
    }

    public static List<Double> doCalculateOBV(List<StockDayData> stockDataList) {
        List<Double> obvList = new ArrayList<>();
        if (stockDataList == null || stockDataList.isEmpty()) {
            return obvList;
        }

        BigDecimal prevClose = stockDataList.get(0).getClosePrice();
        double obv = 0.0;

        for (int i = 0; i < stockDataList.size(); i++) {
            StockDayData currentData = stockDataList.get(i);
            double closePrice = currentData.getClosePrice().doubleValue();
            long volume = currentData.getTradeNum();
            if (i > 0) {
                if (closePrice > prevClose.doubleValue()) {
                    obv += volume;
                } else if (closePrice < prevClose.doubleValue()) {
                    obv -= volume * 1.5;
                }
                // 如果收盘价没有变化，OBV保持不变
            }
            ////使用多空比率净额优化成交量
            //if (i > 0) {
            //    //一字板时还是根据价格高低计算obv
            //    if (BigDecimalUtils.eq(currentData.getMaxPrice(), currentData.getMinPrice())) {
            //        if (BigDecimalUtils.isGe(currentData.getMaxPrice(), prevClose)) {
            //            obv += volume;
            //        } else {
            //            obv -= volume;
            //        }
            //    } else {
            //        double maxPrice = currentData.getMaxPrice().doubleValue();
            //        double minPrice = currentData.getMinPrice().doubleValue();
            //        //多空比率净额= [（收盘价－最低价）－（最高价-收盘价）] ÷（ 最高价－最低价）×V
            //        double v = (closePrice - minPrice) - (maxPrice - closePrice);
            //        double t = maxPrice - minPrice;
            //        v = v / t * volume;
            //        obv += v;
            //    }
            //}
            obvList.add(obv);
            prevClose = currentData.getClosePrice();
        }

        return obvList;
    }

    public static List<Double> calculateExpOBV(List<StockDayData> stockDataList, double alpha) {
        List<Double> obvList = doCalculateOBV(stockDataList);
        List<Double> expObvList = new ArrayList<>();

        if (obvList.isEmpty()) {
            return expObvList;
        }

        double expObv = obvList.get(0);
        expObvList.add(expObv);

        for (int i = 1; i < obvList.size(); i++) {
            expObv = alpha * obvList.get(i) + (1 - alpha) * expObv;
            expObvList.add(expObv);
        }

        return expObvList;
    }

    public static void main(String[] args) {
        // 示例数据，实际数据应该从文件或数据库获取
        List<StockDayData> stockDayDataList = Lists.newArrayList(
            createStockDayData(new BigDecimal("100"), "101", "99.2", 1000000000),
            createStockDayData(new BigDecimal("102"), "102", "99.9", 1200000000),
            createStockDayData(new BigDecimal("105"), "108", "105", 1300000000),
            createStockDayData(new BigDecimal("103"), "104.1", "102.2", 1100000000),
            createStockDayData(new BigDecimal("108"), "108.2", "106", 1500000000),
            createStockDayData(new BigDecimal("110"), "110", "107", 1400000000),
            createStockDayData(new BigDecimal("115"), "115", "112", 1600000000),
            createStockDayData(new BigDecimal("112"), "117", "111", 1700000000),
            createStockDayData(new BigDecimal("118"), "119", "116", 1800000000),
            createStockDayData(new BigDecimal("120"), "122", "119", 1900000000),
            createStockDayData(new BigDecimal("123"), "127", "123", 2000000000),
            createStockDayData(new BigDecimal("119"), "119", "116", 2100000000),
            createStockDayData(new BigDecimal("121"), "122", "121", 2130000000),
            createStockDayData(new BigDecimal("122"), "122.2", "120", 2140000000),
            createStockDayData(new BigDecimal("125"), "127", "124", 2142000000)
        );

        calculateOBV(stockDayDataList);
        System.out.println("OBV: " + JSON.toJSONString(stockDayDataList));
    }

    private static StockDayData createStockDayData(BigDecimal closePrice, String max, String min, int tradeNum) {
        StockDayData stockDayInfo = new StockDayData();
        stockDayInfo.setClosePrice(closePrice);
        stockDayInfo.setMaxPrice(new BigDecimal(max));
        stockDayInfo.setMinPrice(new BigDecimal(min));
        stockDayInfo.setTradeNum(tradeNum);
        return stockDayInfo;
    }
}
